package com.salt.security.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * jwt工具
 *
 * @author HaiBo Kuang
 */
@Service
public class JwtUtil {
    /***
     *  token 加密串，签名串
     */
    private final String SECRET_KEY = "secret";

    /**
     * 解析得到用户名
     *
     * @param token token
     * @return 用户名
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 提取用户token到期时间
     */
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * 提取 Claim 认证信息某一项
     *
     * @param token          token
     * @param claimsResolver 提取方法
     */
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 提取所有 Claim
     *
     * @param token token
     * @return 结果
     */
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    /**
     * token是否过期
     *
     * @param token token
     * @return 结果
     */
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    /**
     * 生成jwt
     *
     * @param userDetails 用户信息
     * @return jwt
     */
    public String generateToken(UserDetails userDetails) {
        // 认证信息 如果您想添加自己的属性，则只需使用map方法，例如 claims.put("someKey", "someValue")
        Map<String, Object> claims = new HashMap<>(16);
        // 用户userName生成token
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                // 设置到期时间 毫秒
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    /**
     * 验证token
     *
     * @param token       token
     * @param userDetails 用户信息
     * @return 结果
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }
}
